#ifndef __wDCHELPER_H__
#define __wDCHELPER_H__

#if !WINDOWS_SYSTEM
	#error only windows system supported
#endif

#ifndef __ETLBASE_H__
#error etldchelper.h requires etlbase.h to be included first
#endif

#if !defined _MSC_VER && !defined __ATLGDI_H__
#error etldchelper.h requires atlgdi.h to be included first
#endif

namespace ETL_GDIAbout
{
//////////////////////////////////////////////////////////////////////////
// wMemDC 
class wMemDC : public CDC
{
public:
	wMemDC(HDC hDC)
	{
		chASSERT(hDC != NULL);
		Attach(::CreateCompatibleDC(hDC));
		chASSERT(m_hDC != NULL);
	}
};

//////////////////////////////////////////////////////////////////////////
// wDCWindowTemp
class wDCWindowTemp : public CDC
{
public:
	wDCWindowTemp(HWND hWnd)
	{
		chASSERT(IsWindow(hWnd));
		m_hFromWnd = hWnd;
		Attach(GetDC(hWnd));
	}
	~wDCWindowTemp()
	{
		ReleaseDC(m_hFromWnd, Detach());
	}
private:
	HWND m_hFromWnd;
};
//////////////////////////////////////////////////////////////////////
// wGdiSelect class
class wGdiSelect
{
public:
	wGdiSelect(HDC hDC, HGDIOBJ hGdiObj)
	{
		chASSERT(hDC != NULL && hGdiObj != NULL);
		m_hOldGdiObj = ::SelectObject(hDC, hGdiObj);
		m_hDC = hDC;
	}
	~wGdiSelect()
	{
		::SelectObject(m_hDC, m_hOldGdiObj);
	}
private:
	HGDIOBJ m_hOldGdiObj;
	HDC m_hDC;
};
//////////////////////////////////////////////////////////////////////////
// wBitmap
class wBitmap : public CBitmap
{
public:
	BOOL BitBltToRect(HDC hDC, CRect& clipRect, UINT uFlag)
	{
		chASSERT(hDC != NULL && m_hBitmap != NULL);
		
		wMemDC memDC(hDC);
		wGdiSelect tGdi(memDC, m_hBitmap);
		SIZE size = GetBitmapPixelSize();
		CPoint pt = clipRect.TopLeft();
		if(uFlag & DT_CENTER)
			pt.x = clipRect.left + (clipRect.Width() - size.cx) / 2;
		else if(uFlag & DT_RIGHT)
			pt.x = clipRect.right - size.cx;
		
		if(uFlag & DT_VCENTER)
			pt.y = clipRect.top + (clipRect.Height() - size.cy) / 2;
		else if(uFlag & DT_BOTTOM)
			pt.y = clipRect.bottom - size.cy;
		
		return ::BitBlt(hDC, pt.x, pt.y, size.cx, size.cy, memDC, 0, 0, SRCCOPY);
	}
	SIZE GetBitmapPixelSize()
	{
		chASSERT(m_hBitmap != NULL);
		BITMAP bm = {0};
		::GetObject(m_hBitmap, sizeof(BITMAP), &bm);
		SIZE tSize = {bm.bmWidth, bm.bmHeight};
		return tSize;
	}
	BOOL BitBltBitmap(HDC hDC, int x, int y, int width = -1, int height = -1)
	{
		chASSERT(hDC != NULL && m_hBitmap != NULL);
		
		wMemDC memDC(hDC);
		wGdiSelect tGdi(memDC, m_hBitmap);
		SIZE size = GetBitmapPixelSize();
		if(width == -1)
			width = size.cx;
		if(height == -1)
			height = size.cy;
		return ::BitBlt(hDC, x, y, width, height, memDC, 0, 0, SRCCOPY);
	}
	
};

//////////////////////////////////////////////////////////////////////////
// wBitmapDC
class wBitmapDC : protected CDC, protected wBitmap
{
public:
	HDC CreateBitmapDC(HDC hDC, CRect& rect)
	{
		m_cx = rect.right - rect.left;
		m_cy = rect.bottom - rect.top;

		chASSERT(hDC != NULL && m_cx > 0 && m_cy > 0);
		chASSERT(CDC::m_hDC == NULL && wBitmap::m_hBitmap == NULL);
		
		CDC::CreateCompatibleDC(hDC);

		wBitmap::CreateCompatibleBitmap(hDC, m_cx, m_cy);
		CDC::SelectBitmap(wBitmap::m_hBitmap);
		
		CDC::BitBlt(0, 0, m_cx, m_cy, hDC, rect.left, rect.top, SRCCOPY);
		return CDC::m_hDC;
	}
	void DeleteDC()
	{
		chASSERT(CDC::m_hDC != NULL && wBitmap::m_hBitmap != NULL);
		CDC::SelectBitmap(NULL);
		wBitmap::DeleteObject();
		CDC::DeleteDC();
	}
	BOOL PasteImageToDC(HDC hDC, CRect& rect)
	{
		chASSERT(hDC != NULL);
		chASSERT(CDC::m_hDC != NULL && wBitmap::m_hBitmap != NULL);
		return ::BitBlt(hDC, rect.left, rect.top, rect.Width(), rect.Height(), CDC::m_hDC, 0, 0, SRCCOPY);
	}
	operator HDC() const { return CDC::m_hDC; }
	operator CDC*() { return this; }
private:
	int m_cx;
	int m_cy;
};

//////////////////////////////////////////////////////////////////////////
// wDataBitmap class
class wDataBitmap : protected wBitmap
{
public:
	operator HBITMAP() const { return m_hBitmap; }
	HBITMAP Detach() {return wBitmap::Detach();}

	HBITMAP CreateBitmapFromDC(HDC hdcFrom, CRect& rect)
	{
		int cx = rect.right - rect.left;
		int cy = rect.bottom - rect.top;
		chASSERT(cx > 0 && cy > 0 && m_pBmpData == NULL);

		wMemDC memOldDC(hdcFrom);

		BITMAPINFO bmi = {0};
		bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
		bmi.bmiHeader.biWidth = cx;
		bmi.bmiHeader.biHeight = cy;
		bmi.bmiHeader.biPlanes = 1;
		bmi.bmiHeader.biBitCount = 32;         // four 8-bit components
		bmi.bmiHeader.biCompression = BI_RGB;
		bmi.bmiHeader.biSizeImage = cx * cy * 4;
		
		m_hBitmap = ::CreateDIBSection(memOldDC.m_hDC, &bmi, DIB_RGB_COLORS, (VOID**)&m_pBmpData, NULL, 0x0);
		chASSERT(m_hBitmap != NULL);

		wGdiSelect tBmpSelect(memOldDC.m_hDC, m_hBitmap);
		::BitBlt(memOldDC.m_hDC, 0, 0, cx, cy, hdcFrom, rect.left, rect.top, SRCCOPY);
		return m_hBitmap;
	}

	BOOL DeleteObject()
	{
		chASSERT(m_pBmpData != NULL);
		m_pBmpData = NULL;

		return wBitmap::DeleteObject();
	}
public:
	wDataBitmap()
	{
		m_pBmpData = NULL;
	}
public:
	DWORD* m_pBmpData;
};

//////////////////////////////////////////////////////////////////////////
// wFileBitmap class
class wFileBitmap : public wBitmap
{
public:
	BOOL LoadFromFile(LPCTSTR lpszFileName, BOOL bRootAsModule = TRUE)
	{
		chASSERT(!chIS_EMPTY(lpszFileName));
		chTempString strImageFile = wGetModuleFilePath(_Module.GetModuleInstance());
		if(bRootAsModule)
		{
			etlPathAppend(strImageFile.GetBuffer(MAX_PATH), lpszFileName);
			lpszFileName = strImageFile;
		}
		
		HBITMAP hBitmap = (HBITMAP)::LoadImage(_Module.GetModuleInstance(), lpszFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
//		HBITMAP hBitmap = (HBITMAP)::LoadImage(NULL, lpszFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

//		BITMAP bm = {0};
//		::GetObject(hBitmap, sizeof(BITMAP), &bm);
//		SYZ_TRACE4("bmpSize = %.8X(%s) = {%d, %d}", hBitmap, lpszFileName, bm.bmWidth, bm.bmHeight);

		Attach(hBitmap);
		return hBitmap != NULL;
	}
public:
	wFileBitmap(LPCTSTR lpszFileName = NULL, BOOL bRootAsModule = TRUE)
	{
		if(!chIS_EMPTY(lpszFileName))
			LoadFromFile(lpszFileName, bRootAsModule);
	}
};

inline SIZE wGetBitmapPixelSize(HBITMAP hBitmap)
{
	chASSERT(hBitmap != NULL);
	BITMAP bm = {0};
	::GetObject(hBitmap, sizeof(BITMAP), &bm);
	SIZE tSize = {bm.bmWidth, bm.bmHeight};
	return tSize;
}

inline BOOL wDrawBitmap(HBITMAP hBitmap, HDC hDC, int x, int y)
{
	chASSERT(hDC != NULL && hBitmap != NULL);
	
	wMemDC memDC(hDC);
	wGdiSelect tGdi(memDC, hBitmap);
	SIZE size = wGetBitmapPixelSize(hBitmap);
	return ::BitBlt(hDC, x, y, size.cx, size.cy, memDC, 0, 0, SRCCOPY);
}

inline BOOL wDrawBitmapInRect(HBITMAP hBitmap, HDC hDC, CRect& clipRect, UINT uFlag)
{
	chASSERT(hDC != NULL && hBitmap != NULL);
	
	wMemDC memDC(hDC);
	wGdiSelect tGdi(memDC, hBitmap);
	SIZE size = wGetBitmapPixelSize(hBitmap);
	CPoint pt = clipRect.TopLeft();
	if(uFlag & DT_CENTER)
		pt.x = clipRect.left + (clipRect.Width() - size.cx) / 2;
	else if(uFlag & DT_RIGHT)
		pt.x = clipRect.right - size.cx;

	if(uFlag & DT_VCENTER)
		pt.y = clipRect.top + (clipRect.Height() - size.cy) / 2;
	else if(uFlag & DT_BOTTOM)
		pt.y = clipRect.bottom - size.cy;
		
	return ::BitBlt(hDC, pt.x, pt.y, size.cx, size.cy, memDC, 0, 0, SRCCOPY);
}

inline HBITMAP wCreateGradientBMP(int cx, int cy, BOOL bHor, COLORREF colTL, COLORREF colBR)
{
	chASSERT(cx > 0 && cy > 0 && cx * cy < 0x400000); // max size is 16M
	
	chDWORDArray bmpBuf((DWORD)0, cx * cy);
	COLORREF* pBuf = (COLORREF*)bmpBuf.GetData();
	int R_0 = GetRValue(colTL); int R_1 = GetRValue(colBR); int dR = R_1 - R_0;
	int G_0 = GetGValue(colTL); int G_1 = GetGValue(colBR); int dG = G_1 - G_0;
	int B_0 = GetBValue(colTL); int B_1 = GetBValue(colBR); int dB = B_1 - B_0;
	if(bHor)
	{
		int i = 0;
		for(i = 0; i < cx; i++)
		{
			pBuf[i] = RGB(MulDiv(dR, i, cx) + R_0,
				MulDiv(dG, i, cx) + G_0,
				MulDiv(dB, i, cx) + B_0);
		}
		int j = 0;
		for(j = 1; j < cy; j++)
			memcpy(pBuf + j * cx, pBuf, cx * sizeof(COLORREF));
	}
	else
	{
		int j = 0;
		for(j = 0; j < cy; j++)
		{
			COLORREF Col = RGB(MulDiv(dR, j, cy) + R_0,
				MulDiv(dG, j, cy) + G_0,
				MulDiv(dB, j, cy) + B_0);
			COLORREF* pBase = pBuf + j * cx;
			int i = 0;
			for(i = 0; i < cx; i++)
				pBase[i] = Col;
		}
	}
	return ::CreateBitmap(cx, cy, 1, 32, pBuf);
}

inline HBITMAP wCreateDCBMP(HDC hDC, int x, int y, int cx, int cy)
{
	chASSERT(hDC != NULL);
	
	wMemDC memDC(hDC);
	HBITMAP hBitmap = ::CreateCompatibleBitmap(hDC, cx, cy);
	wGdiSelect tGdi(memDC, hBitmap);

	::BitBlt(memDC, 0, 0, cx, cy, hDC, x, y, SRCCOPY);
	return hBitmap;
}

inline HBITMAP wCreateSmallBMP(HBITMAP hBmp, int x, int y, int cx, int cy)
{
	chASSERT(cx > 0 && cy > 0 && x >= 0 && y >= 0);
	
//	LCDWORDArray bigBuffer, smallBuffer;
//	LwBitmap bigBitmap;bigBitmap.Attach(hBmp);
//	CSize bigSize = bigBitmap.wGetBitmapPixelSize();
//	
//	if(x >= bigSize.cx || y >= bigSize.cy)
//		return FALSE;
//	int width = min(cx, bigSize.cx - x);
//	int height = min(cy, bigSize.cy - y);
//	if(width * height >= 0x400000) // max size is 16M
//		return FALSE;
//	
//	DWORD nSize = bigSize.cy * bigSize.cx;
//	bigBuffer.SetSize(nSize);
//	::GetBitmapBits(hBmp, nSize * 4, bigBuffer.GetData());
//	
//	smallBuffer.SetSize(width * height);
//	DWORD* pSrc = bigBuffer.GetData() + bigSize.cx * y + x;
//	DWORD* pDes = smallBuffer.GetData();
//	for(int i = 0; i < height; i++)
//	{
//		memcpy(pDes, pSrc, width * 4);
//		pDes += width;
//		pSrc += bigSize.cx;
//	}
//	return ::CreateBitmap(width, height, 1, 32, smallBuffer.GetData());
	return FALSE;
}

//inline chTempDWORDArray wGetBmpData(HBITMAP hBitmap)
//{
//	chASSERT(hBitmap != NULL);
//
//	BITMAP bm = {0};
//	::GetObject(hBitmap, sizeof(BITMAP), &bm);
//	
//	chTempDWORDArray arrBmpData((DWORD)0, bm.bmHeight * bm.bmWidth);
//	
//	return arrBmpData;
//}

inline BOOL wDrawBitmapTrans(HBITMAP hBitmap, HDC hDC, int x, int y, COLORREF rTrans)
{
	chASSERT(hDC != NULL && hBitmap != NULL);
	wMemDC memDC(hDC);
	wGdiSelect tGdi(memDC, hBitmap);
	
	SIZE size = wGetBitmapPixelSize(hBitmap);
	return ::TransparentBlt(hDC, x, y, size.cx, size.cy, memDC, 0, 0, size.cx, size.cy, rTrans);
}

inline int wGrayText(HDC hDC, LPCTSTR strText, int nCount, LPRECT lpRect, UINT nFormat)
{
	chASSERT(hDC != NULL && strText != NULL);
	int oldMode = ::SetBkMode(hDC, TRANSPARENT);
	COLORREF oldCol = ::SetTextColor(hDC, GetSysColor(COLOR_GRAYTEXT));
	RECT Rect = *lpRect;
	int nRe = ::DrawText(hDC, strText, nCount, &Rect, nFormat);
	::OffsetRect(&Rect, 1, 1);
	::SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
	nRe = ::DrawText(hDC, strText, nCount, &Rect, nFormat);
	
	::SetTextColor(hDC, oldCol);
	::SetBkMode(hDC, oldMode);
	return nRe;
}

};
using namespace ETL_GDIAbout;

#endif //__wDCHELPER_H__